package top.honlnk.czuan.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import top.honlnk.czuan.common.advisor.ChatMemoryAdvisor;
import top.honlnk.czuan.common.utils.AliOSSUtil;
import top.honlnk.czuan.common.utils.SnowflakeIdUtil;
import top.honlnk.czuan.mapper.dict.ExplainOssUrlMapper;
import top.honlnk.czuan.pojo.entity.AiWordExplain;
import top.honlnk.czuan.service.AiService;

import java.util.List;

@Slf4j
@Service
public class AiServiceImpl implements AiService {

    @Autowired
    private AliOSSUtil aliOSSUtil;

    @Autowired
    private SnowflakeIdUtil snowflakeIdUtil;

    @Autowired
    private ExplainOssUrlMapper explainOssUrlMapper;

    private final ChatClient chatClient;

    private Integer makeOnlyId() {
        return snowflakeIdUtil.nextId();
    }

    public AiServiceImpl(ChatClient.Builder builder, ChatMemoryAdvisor chatMemoryAdvisor) {
        this.chatClient = builder
                .defaultSystem("""
                        您是一个单词辅助记忆助手，您的主要负责对词典API返回结果进行分析，
                        API返回的结果是一个json数据，里面包含一定数量的与次单词有关的知识内容，
                        您需要阅读这些json数据，将里面数据以用户可视的markdown格式的文本返回。
                        您只需要返回与单词有关的文本即可，无需返回多余的内容，以免影响用户阅读文章。
                        关于解读json数据，其中有几个重点你需要注意：
                        1. tr_group中的的属性，tr属性是这个单词的这一个分组的其中一个解释，pos表示的是这个解释对应的翻译的词性。
                        2. json中包含大量的tr_group数据，这也是json数据的重点。
                        3. 这些group数据呗一个大的edict属性包围，进一步解释了这些分组是对这个单词翻译的不同的区分。
                        4. 你需要对这些英文解释使用中文再来解释一遍，比如："the act of mixing cards haphazardly"可以解释为："指的是将牌无序地混合的行为，通常是在纸牌游戏开始之前进行以确保游戏的公平性。"
                        5. 在解释这些已经分类好的英文解释之前，你要根据你自己的理解，对这些解释进行一个综合解释，用一句话表达出这个单词要表达什么意思。比如：对单词："on"的解释：on的翻译可以有很多，但是它表达的含义可以归类于它要描述的那个事物于另一个事物存在一个在“上面”且紧贴着的位置关系。
                        6. 每个解释都跟有一个例句，你需要对这些例句进行翻译，并讲解查询的这个单词在这个句子中的作用。
                        7. 在simple_means属性中的的from属性，它表述的是这个单词的来源，你需要根据你的理解对这个属性做一个解释。并且由于这个属性的字符数量较小，所以推荐你将这个解释放在文章靠上的位置最好在前6条中提到的解释的前面。
                        8. 如果json中出现对这个单词的具体翻译，你可以忽略掉这些，因为系统在别处已经处理了这些数据。
                        9. 关于exchange属性，这个属性表示的是与这个单词相似的单词，基本都是这个次的其他变形式。你需要对这些单词用一两句话解释与查询的这个单词的关系。
                        以下是一个你要输出的模板：
                        
                        ## 关于单词 "make" 的解释
                        
                        ### 来源解释
                        "make" 这个单词来源于原始语言，常用于高考、CET4、考研等考试中。此外，它还出现在BEC（商务英语证书）考试中。
                        
                        ### 概况
                        "make" 的翻译可以有很多，但它的核心含义可以归结为 **通过某种行为或操作，使某物或某人发生变化或达到某种状态**。
                        
                        ### 详细解释
                        1. **mixing cards haphazardly** \s
                           - **词性**：名词
                           - **解释**：指的是将牌无序地混合的行为，通常是在纸牌游戏开始之前进行以确保游戏的公平性。
                           - **例句**：没有例句。
                           - **相似词**：shuffle, shuffling
                        
                        2. **a recognizable kind** \s
                           - **词性**：名词
                           - **解释**：指的是一种可以识别的类型。
                           - **例句**：
                             - there's a new brand of hero in the movies now（电影里有一种新的英雄形象）
                                - brand代替make，如果要将 "make" 放入类似的语境中，可以说："There's a new make of hero in the movies now." 不过这样的表达不如用 "brand" 自然。通常我们会说 "make" 是用于询问或描述车辆、机械等物品的品牌或型号。因此，虽然 "make" 和 "brand" 在某些情况下可以互换使用，但它们并非总是完全同义，选择哪个词取决于具体的上下文和习惯用法。
                             - what make of car is that?（那是什么牌子的车？）
                           - **相似词**：brand
                        3. 。。。。
                        
                        ### 单词变形式
                        1. make (三单形式)：makes
                        2. make (现在分词形式)：making
                        3. make (过去式/过去分词形式)：made
                        4. 。。。
                        
                        这些变体形式主要用于语法上的不同需求，如第三人称单数、现在进行时和过去时态的使用。
                        """)

                .defaultAdvisors(
                        new PromptChatMemoryAdvisor(chatMemoryAdvisor.getChatMemory())
                )
                .build();
    }

    public Flux<String> chatStream(String fileUrl) {

        log.info("用户输文件地址：{}", fileUrl);
        JSONObject originDate = aliOSSUtil.getDictFile(fileUrl);
        log.info("用户输入的json数据：{}", originDate);

        StringBuilder resultData = new StringBuilder();

        // 可选：实时存储中间结果
        return chatClient.prompt()
                .user(JSONUtil.toJsonStr(originDate))
//              .system(promptSystemSpec -> promptSystemSpec.param("word", input)) // 假设要获取单词的翻译
                .advisors(advisorSpec -> advisorSpec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 5)) // 记忆的上线问长度
                .stream()
                .content()
//                .doOnNext(response -> log.info("流式响应内容：{}", response)) // 每次有新内容时记录日志
                .doOnNext(resultData::append)
                .doFinally(signalType -> {
                    String finalResult = resultData.toString();
                    storageData(finalResult, fileUrl);
                });
    }

    private void storageData(String finalResult, String fileUrl) {

        String word = "";

        try {
            // 解析URL路径
            String path = new java.net.URL(fileUrl).getPath();

            // 移除路径前缀（如 dict/json/baidu/）和后缀 .json
            String fileName = path.replaceAll(".*/", "").replaceFirst("\\.json$", "");

            // 通过 "--" 分割获取原始 word
            String[] parts = fileName.split("--", 2);

            // 验证格式有效性
            if (parts.length < 1 || parts[0].isEmpty()) {
                throw new IllegalArgumentException("Invalid file name format: " + fileName);
            }
            log.info("原始单词：{}", parts[0]);
            word = parts[0];


        } catch (Exception e) {
            log.error("单词名解析失败", e);
        }
        String markdownFileUrl = aliOSSUtil.uploadMarkdownFile(finalResult, word);

        // 存储当前路径
        log.info("存储当前路径：{}", markdownFileUrl);
        AiWordExplain aiWordExplain = AiWordExplain.builder()
                .id(makeOnlyId())
                .word(word)
                .dataOssUrl(markdownFileUrl)
                .build();

        explainOssUrlMapper.addExplain(aiWordExplain);
    }

    public List<AiWordExplain> isExist(String fileUrl) {
        return explainOssUrlMapper.getExplainByWord(fileUrl);
    }
}
